/*---------------------------------------------------------------------------
Remotes.m -- Copyright (c) 1991 Rex Pruess
  
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or send
   electronic mail to the the author.
  
This routine contains the methods for the main menu and for the hosts window.
Seldom used objects are only created if necessary.  This reduces the
application's initial load time.
  
Rex Pruess <rpruess@umaxc.weeg.uiowa.edu>
  
$Header: /rpruess/apps/Remotes3.0/RCS/Remotes.m,v 3.0 92/09/23 22:15:47 rpruess Exp $
-----------------------------------------------------------------------------
$Log:	Remotes.m,v $
Revision 3.0  92/09/23  22:15:47  rpruess
Checked in to RCS to get the revision number updated to 3.0.

Revision 2.1  92/09/23  21:29:55  rpruess
Updated code for NeXT System Release 3.0.

Revision 2.0  91/01/22  15:33:01  rpruess
Remotes-2.0 was upgraded for NeXT System Release 2.0 (standard or extended).
Remotes-2.0 supports the NeXT supplied Terminal application and the Stuart
shareware product.

Revision 1.3  90/05/17  15:02:35  rpruess
Added windowWillResize code to limit minimum/maximum size of the hosts
window.

Revision 1.2  90/05/16  09:34:17  rpruess
Added signal handling routine in order to catch the termination of child
processes.  Without this code, a "defunct" process remains on the system
whenever a child process exits.

Revision 1.1  90/04/10  14:27:32  rpruess
Initial revision

-----------------------------------------------------------------------------*/

/* Standard C header files */
#include <libc.h>
#include <signal.h>
#include <stdio.h>
#include <strings.h>

/* Remotes class header files */
#import "Remotes.h"
#import "Configure.h"
#import "GNULicense.h"
#import "Help.h"
#import "HostListManager.h"
#import "Info.h"
#import "Prefs.h"

/* Appkit header files */
#import <appkit/Application.h>
#import <appkit/Button.h>
#import <appkit/PopUpList.h>

#import <defaults/defaults.h>

@implementation Remotes

/*---------------------------------------------------------------------------
In order to prevent defunct child processes, we must reap them.
-----------------------------------------------------------------------------*/
void reap_child()
{
   int pid;
   union wait status;
   while((pid = wait3(&status, WNOHANG, (struct rusage *) 0)) > 0);
}

/*---------------------------------------------------------------------------
Override the new method so the objects are created in the proper order.  The
creation of the HLM object results in the loading of the configuration file by
one of the HLM methods.  The hosts popup list object is created here too.
-----------------------------------------------------------------------------*/
- init
{
   self = [super init];
   
   myConfig = nil;              /* Seldom used objects start as nil. */
   myGNULicense = nil;
   myHelpPanel = nil;
   myInfoPanel = nil;
   myPrefs = nil;

   myHLM = [[HostListManager alloc] init];
   hostsPUL = [[PopUpList alloc] init];

   [NXApp setDelegate:self];

   return self;
}

/*---------------------------------------------------------------------------
Override the initialize method so the defaults can be registered.
-----------------------------------------------------------------------------*/
+ initialize
{
   char            configFile[MAXCONFIGLEN + 1];
   int             i;

   static NXDefaultsVector RemotesDefaults =
   {
      {"ConfigFile", NULL},
      {"Width", NULL},
      {"Height", NULL},
      {"DebugLevel", "off"},
      {"WinLocX", "400.0"},
      {"WinLocY", "700.0"},
      {NULL}
   };

   static NXDefaultsVector StuartDefaults =
   {
      {"Columns", "80"},
      {"Lines", "24"},
      {"NXFixedPitchFont", "Ohlfs"},
      {"NXFixedPitchFontSize", "10"},
      {NULL}
   };

   static NXDefaultsVector TerminalDefaults =
   {
      {"Columns", "80"},
      {"Rows", "24"},
      {"NXFixedPitchFont", "Ohlfs"},
      {"NXFixedPitchFontSize", "10"},
      {NULL}
   };

   /* Register the Stuart/Terminal defaults.  These are easy. */
   NXRegisterDefaults ("Stuart", StuartDefaults);
   NXRegisterDefaults ("Terminal", TerminalDefaults);

   /* Set up the config file default pathname (e.g., /userid/.Remotes). */
   strcpy (configFile, NXHomeDirectory ());
   strcat (configFile, "/.Remotes");

   /* Assign storage & stuff the config pathname into it. */
   RemotesDefaults[0].value = malloc (strlen (configFile) + 1);
   strcpy (RemotesDefaults[0].value, configFile);

   /* Stuff in default hosts window width/height */
   RemotesDefaults[1].value = malloc (16);
   sprintf (RemotesDefaults[1].value, "%.1f", DEFWIDTH);

   RemotesDefaults[2].value = malloc (16);
   sprintf (RemotesDefaults[2].value, "%.1f", DEFHEIGHT);

   /* Register the Remotes defaults & release the storage. */
   NXRegisterDefaults ([NXApp appName], RemotesDefaults);

   for (i = 0; i <= 2; i++)
      free (RemotesDefaults[i].value);

   return self;
}

/*---------------------------------------------------------------------------
Application did initialize.  Get the previous size & location of the hosts
window and display it the same way.  Fire up any terminal/shells that must be
automatically started.
-----------------------------------------------------------------------------*/
- appDidInit:sender
{
   NXSize          screenSize;  /* Screen width/height */
   const char     *string;      /* Temporary variable */
   int             xMax;        /* Maximum X value allowed */
   int             yMax;        /* Maximum Y value allowed */
   NXRect          windowRect;  /* Coordinates & size of window */

   /* Get screen size & assign maximum values allowable. */
   [NXApp getScreenSize:&screenSize];
   xMax = screenSize.width - 5;
   yMax = screenSize.height - 5;

   /* Set x, y, width, & height values for the hosts window. */
   string = NXGetDefaultValue ("Remotes", "WinLocX");
   windowRect.origin.x = atof (string);
   if (windowRect.origin.x < 0)
      windowRect.origin.x = 0;
   if (windowRect.origin.x > xMax)
      windowRect.origin.x = xMax;

   string = NXGetDefaultValue ("Remotes", "WinLocY");
   windowRect.origin.y = atof (string);
   if (windowRect.origin.y < 0)
      windowRect.origin.y = 0;
   if (windowRect.origin.y > yMax)
      windowRect.origin.y = yMax;

   string = NXGetDefaultValue ("Remotes", "Width");
   windowRect.size.width = atof (string);
   if (windowRect.size.width < MINWIDTH ||
      windowRect.size.width > MAXWIDTH)
      windowRect.size.width = DEFWIDTH;

   string = NXGetDefaultValue ("Remotes", "Height");
   windowRect.size.height = atof (string);
   if (windowRect.size.height < MINHEIGHT ||
      windowRect.size.height > MAXHEIGHT)
      windowRect.size.height = DEFHEIGHT;

   /* Position the hosts window & display it on the screen. */
   [hostsWindow placeWindow:&windowRect];
   [hostsWindow makeKeyAndOrderFront:self];

   /* Assign icon for when window is miniturized. */
   [hostsWindow setMiniwindowIcon:"app"];

   /* Fire up those autostart guys. */
   [myHLM autoStart:sender];

   signal(SIGCHLD, reap_child);
   
   return self;
}

/*---------------------------------------------------------------------------
Display the GNU license.
-----------------------------------------------------------------------------*/
- showGNULicense:sender
{
   if (myGNULicense == nil)
      myGNULicense = [[GNULicense alloc] init];

   /* Not really necessary, but kinda cutesy. */
   if ([sender isKindOf:[Control class]] == YES) {
      [sender setTarget:myGNULicense];
      [sender setAction:@selector (showLicense:)];
   }

   [myGNULicense showLicense:self];
   return self;
}

/*---------------------------------------------------------------------------
Display the help panel.
-----------------------------------------------------------------------------*/
- showHelp:sender
{
   if (myHelpPanel == nil)
      myHelpPanel = [[Help alloc] init];

   [myHelpPanel showHelpPanel:self];

   return self;
}

/*---------------------------------------------------------------------------
Display the info panel.
-----------------------------------------------------------------------------*/
- showInfo:sender
{
   if (myInfoPanel == nil)
      myInfoPanel = [[Info alloc] init];

   [myInfoPanel showInfoPanel:self];

   return self;
}

/*---------------------------------------------------------------------------
Fire up the configuration window.
-----------------------------------------------------------------------------*/
- showConfigWindow:sender
{
   if (myConfig == nil) {
      myConfig = [[Configure alloc] init];
      [myConfig setHLM:myHLM];
      [myConfig setHostsButton:hostsButton];
      [myConfig setHostsPUL:hostsPUL];
      [myConfig initConfigWindow:sender];
      [myConfig initConfigButton:sender];
   }

   [myConfig showConfigWindow:self];

   return self;
}

/*---------------------------------------------------------------------------
Fire up the preferences window.
-----------------------------------------------------------------------------*/
- showPrefsWindow:sender
{
   if (myPrefs == nil) {
      myPrefs = [[Prefs alloc] init];
      [myPrefs setHLM:myHLM];
      [myPrefs initPrefs:sender];
   }

   [myPrefs showPrefsWindow:self];

   return self;
}

/*---------------------------------------------------------------------------
Handle login request from the hosts window.
-----------------------------------------------------------------------------*/
- loginNow:sender
{
   [myHLM loginToHost:[[sender selectedCell] title] activate:YES];
   return self;
}

/*---------------------------------------------------------------------------
Revert fields in the configuration window or the preferences window.  The
methods in each object will check to see if the window is the key window.
If so, the fields will be reverted.
-----------------------------------------------------------------------------*/
- menuRevert:sender
{
   if (myConfig != nil)
      [myConfig menuRevert:self];

   if (myPrefs != nil)
      [myPrefs menuRevert:self];

   return self;
}

/*---------------------------------------------------------------------------
Hosts window moved so save the new coordinates/size.
-----------------------------------------------------------------------------*/
- windowDidMove:sender
{
   [self windowUpdateDefaults:self];
   return self;
}

/*---------------------------------------------------------------------------
Hosts window will resize so validate acceptable values.
-----------------------------------------------------------------------------*/
- windowWillResize:sender toSize:(NXSize *)frameSize
{
    if (frameSize->width < MINWIDTH) frameSize->width = MINWIDTH;
    if (frameSize->width > MAXWIDTH) frameSize->width = MAXWIDTH;

    if (frameSize->height < MINHEIGHT) frameSize->height = MINHEIGHT;
    if (frameSize->height > MAXHEIGHT) frameSize->height = MAXHEIGHT;
    
    return self;
}

/*---------------------------------------------------------------------------
Hosts window resized so save the new coordinates/size.
-----------------------------------------------------------------------------*/
- windowDidResize:sender
{
   [self windowUpdateDefaults:self];
   return self;
}

/*---------------------------------------------------------------------------
Get the new hosts window frame values & save them in the defaults data base.
-----------------------------------------------------------------------------*/
- windowUpdateDefaults:sender
{
   int             i;
   char            string[80];  /* Temporary variable */
   NXRect          windowRect;  /* Coordinates & size of window */

   NXDefaultsVector remotesV =
   {
      {"WinLocX", NULL},
      {"WinLocY", NULL},
      {"Width", NULL},
      {"Height", NULL},
      {NULL}
   };

   [hostsWindow getFrame:&windowRect];

   sprintf (string, "%.1f", windowRect.origin.x);
   remotesV[0].value = malloc (strlen (string) + 1);
   strcpy (remotesV[0].value, string);

   sprintf (string, "%.1f", windowRect.origin.y);
   remotesV[1].value = malloc (strlen (string) + 1);
   strcpy (remotesV[1].value, string);

   sprintf (string, "%.1f", windowRect.size.width);
   remotesV[2].value = malloc (strlen (string) + 1);
   strcpy (remotesV[2].value, string);

   sprintf (string, "%.1f", windowRect.size.height);
   remotesV[3].value = malloc (strlen (string) + 1);
   strcpy (remotesV[3].value, string);

   NXWriteDefaults ([NXApp appName], remotesV);

   for (i = 0; i <= 3; i++)
      free (remotesV[i].value);

   return self;
}

/*---------------------------------------------------------------------------
Hosts window.
-----------------------------------------------------------------------------*/
- setHostsWindow:anObject
{
   hostsWindow = anObject;
   return self;
}

/*---------------------------------------------------------------------------
Set up the hosts popup button & its associated popup list.
-----------------------------------------------------------------------------*/
- setHostsButton:anObject
{
   hostsButton = anObject;

   [myHLM loadPopUpList:hostsPUL];

   [hostsButton setTarget:hostsPUL];
   [hostsButton setAction:@selector (popUp:)];

   NXAttachPopUpList (hostsButton, hostsPUL);

   [hostsPUL setTarget:self];
   [hostsPUL setAction:@selector (loginNow:)];

   return self;
}

@end
